package com.zslibrary.Protocol;

import android.os.SystemClock;
import android.util.SparseArray;

import com.zslibrary.Enum.CommunicationError;
import com.zslibrary.Enum.KeyTypeDefine;
import com.zslibrary.Enum.PipeResultDefine;
import com.zslibrary.Enum.Protocol;
import com.zslibrary.Enum.StateEventDefine;
import com.zslibrary.Enum.TestTypeDefine;
import com.zslibrary.Pipe.Pipe;
import com.zslibrary.Structure.ComponentStruct;
import com.zslibrary.Structure.GeneralRequestStruct;
import com.zslibrary.Structure.LocalAdjustmentStruct;
import com.zslibrary.Structure.MessageStruct;
import com.zslibrary.Structure.ReplyStruct;
import com.zslibrary.Structure.TestInfosStruct;
import com.zslibrary.Structure.TestResultStruct;
import com.zslibrary.Structure.UserAdjustmentStruct;
import com.zslibrary.Tools.FrameBuilder;
import com.zslibrary.Tools.HexBytesUtil;
import com.zslibrary.Tools.ZSLog;

import java.util.ArrayList;
import java.util.List;

import static com.zslibrary.Protocol.Machine.getComponentList;

public class ComponentTest {
    private Boolean isBegin=false;
    private Boolean isEnd=false;
    private Boolean isCancel=false;
    private Boolean isTesting=false;
    private Boolean mTestResult=true;
    private TestResultStruct[] mDynamicTestResults=null;
    private UserAdjustmentStruct mCalibrationValue=null;
    private TestInfosStruct[] mTestInfos=null;
    private List<String> mTestStrings=new ArrayList<>();
    private short mDurationComponent=0;
    private short mDurationFunction=0;
    private List<TestInfosStruct> mDynamicTestInfos=new ArrayList<>();

    public @StateEventDefine.StateEvent int componentTest(final Pipe pipe, short componentId, short funcId){
        GeneralRequestStruct request=new GeneralRequestStruct();
        FrameBuilder dataBuilder=new FrameBuilder(4);
        int timeOut=90000;

        /*如果当前为duration test*/
        if(mTestInfos!=null){
            for(TestInfosStruct info:mTestInfos){
                if((info.index==funcId)&&(info.type== TestTypeDefine.DURAT_TEST)){
                    mDurationComponent=componentId;
                    mDurationFunction=funcId;
                    timeOut=0;
                    break;
                }
            }
        }
        dataBuilder.append(componentId);
        dataBuilder.append(funcId);
        request.requstId= Protocol.MSG_RUN_COMPONENT_TEST;
        request.dataLength=4;
        request.data=dataBuilder.toBytes();
        Pipe.HandleMessageCallback callback=new Pipe.HandleMessageCallback() {
            @Override
            public Boolean handle(MessageStruct[] messages) {
                for(MessageStruct message:messages){
                    switch(message.messageId){
                        case Protocol.MSG_SET_COMP_ERR:{
                            CommonMessage.processSetError(message);
                        }break;

                        case Protocol.MSG_CLEAR_COMP_ERR:{
                            CommonMessage.processClrError(message);
                        }break;

                        case Protocol.MSG_BOARD_RESET:{
                            pipe.interruptAsynchonousPipe();    /*可被终止*/
                        }break;

                        case Protocol.MSG_REPORT_BEGIN: {
                            ZSLog.d("test begin");
                            isBegin = true;
                        }break;

                        case Protocol.MSG_REPORT_END: {
                            ZSLog.d("test end");
                            FrameBuilder builder=new FrameBuilder(message.messageData);
                            int result=builder.popData(4);
                            mTestResult=(result==1)?true:mTestResult;
                            isEnd = true;
                        }break;

                        case Protocol.MSG_REPORT_STR: {
                            String reportStr = new String(message.messageData);
                            mTestStrings.add(reportStr);
                            //ZSLog.d("report string:" + reportStr);
                        }break;

                        case Protocol.MSG_BOARD_STATUS:{
                            //ZSLog.d("report status:"+HexBytesUtil.Bytes2Int(message.messageData));
                        }break;

                        default:
                            break;
                    }
                }
                if(isBegin&&isEnd)
                    return true;
                return false;
            }
        };
        isBegin=false;
        isEnd=false;
        isCancel=false;
        isTesting=true;
        mTestStrings.clear();
        @PipeResultDefine.PipeResult int result=pipe.asynchronousPipe(request,callback,timeOut);
        isEnd=true;
        isTesting=false;
        if(result==PipeResultDefine.ASYNC_ERROR)
            return StateEventDefine.EVT_DISCONNECT;
        else if((result==PipeResultDefine.ASYNC_INTERRUPT)&&!isCancel)
            return StateEventDefine.EVT_RESET;
        else if(result==PipeResultDefine.ASYNC_BUSY)
            return StateEventDefine.EVT_EXECUTE;
        else if(result==PipeResultDefine.ASYNC_OK)
            return StateEventDefine.EVT_NONE;
        return StateEventDefine.EVT_NONE;
    }

    public int getTestStatus(){
        if((!isBegin)&&(!isEnd))
            return 0;   //未开始
        if((!isBegin)&&isEnd)
            return -1;  //异常，很难出现
        if(isBegin&&(!isEnd))

            return 1;   //进行
        if(isBegin&&isEnd)
            return 2;   //结束
        return 0;
    }

    private Boolean getTestResult(){
        return mTestResult;
    }

    public String[] getTestString(){
        return mTestStrings.toArray(new String[mTestStrings.size()]);
    }

    public @StateEventDefine.StateEvent int putCalibrationValue(Pipe pipe, short componentId, int times, int inValue){
        LocalAdjustmentStruct lAdj= Adjustments.getCalibrationAdjustment(componentId);
        mCalibrationValue=null;
        if(lAdj==null)
            return StateEventDefine.EVT_NONE;
        UserAdjustmentStruct uAdj=new UserAdjustmentStruct();
        uAdj.componentId=componentId;
        uAdj.adjustmentId=(byte)lAdj.adjustmentId;
        uAdj.fluid=0;
        uAdj.productId=-1;
        uAdj.value=(short)(inValue/times);
        int result= Adjustment.addUserAdjustment(pipe,uAdj);
        if(result==StateEventDefine.EVT_NONE)
            mCalibrationValue=uAdj;
        return result;
    }

    public UserAdjustmentStruct putCalibrationValue(){
        return mCalibrationValue;
    }

    public @StateEventDefine.StateEvent int cancelDurationTest(final Pipe pipe, short componentId, short functionId){
        if((componentId==mDurationComponent)&&(functionId==mDurationFunction)){
            GeneralRequestStruct request=new GeneralRequestStruct();
            FrameBuilder dataBuilder=new FrameBuilder(1);
            dataBuilder.append((byte) KeyTypeDefine.KEY_CANCEL);
            request.requstId=Protocol.MSG_SEND_KEY;
            request.dataLength=1;
            request.data=dataBuilder.toBytes();
            Pipe.HandleMessageCallback callback=new Pipe.HandleMessageCallback() {
                @Override
                public Boolean handle(MessageStruct[] messages) {
                    for(MessageStruct message:messages){
                        switch(message.messageId){
                            case Protocol.MSG_SET_COMP_ERR:{
                                CommonMessage.processSetError(message);
                            }break;

                            case Protocol.MSG_CLEAR_COMP_ERR:{
                                CommonMessage.processClrError(message);
                            }break;

                            case Protocol.MSG_BOARD_RESET:{
                                pipe.interruptAsynchonousPipe();    /*可被终止*/
                            }break;

//                            case Protocol.MSG_REPORT_BEGIN: {
//                                ZSLog.d("test begin");
//                                isBegin = true;
//                            }break;

                            case Protocol.MSG_REPORT_END: {
                                ZSLog.d("test end");
                                isEnd = true;
                            }break;

                            case Protocol.MSG_REPORT_STR: {
                                String reportStr = new String(message.messageData);
                                //ZSLog.d("report string:" + reportStr);

                            }break;

                            case Protocol.MSG_BOARD_STATUS:{
                                //ZSLog.d("report status:"+HexBytesUtil.Bytes2Int(message.messageData));
                            }break;

                            default:
                                break;
                        }
                    }
                    if(isEnd)
                        return true;
                    return false;
                }
            };
            @PipeResultDefine.PipeResult int result=pipe.asynchronousPipe(request,callback,0);  //检测是否停止成功
            if(result==PipeResultDefine.ASYNC_ERROR)
                return StateEventDefine.EVT_DISCONNECT;
            else if(result==PipeResultDefine.ASYNC_INTERRUPT)
                return StateEventDefine.EVT_RESET;
            else if(result==PipeResultDefine.ASYNC_BUSY)
                return StateEventDefine.EVT_EXECUTE;
            else if(result==PipeResultDefine.ASYNC_OK)
                return StateEventDefine.EVT_NONE;
            return StateEventDefine.EVT_NONE;
        }
        return StateEventDefine.EVT_NONE;
    }

    public void shutDownComponentTest(Pipe pipe){
        if(isTesting){
            isCancel=true;
            ZSLog.d("shut down test!");
            pipe.interruptAsynchonousPipe();
            while(isTesting){
                SystemClock.sleep(10);
            }
        }
    }

    public @StateEventDefine.StateEvent int getTestInfos(Pipe pipe, final short componentId){
        GeneralRequestStruct request=new GeneralRequestStruct();
        FrameBuilder dataBuilder=new FrameBuilder(2);

        mTestInfos=null;
        dataBuilder.append(componentId);
        request.requstId=Protocol.MSG_GET_TEST_INFOS;
        request.dataLength=2;
        request.data=dataBuilder.toBytes();
        Pipe.HandleMessageCallback callback=new Pipe.HandleMessageCallback() {
            @Override
            public Boolean handle(MessageStruct[] messages) {
                List<TestInfosStruct> infos=new ArrayList<>();
                for (MessageStruct message : messages) {
                    FrameBuilder builder = new FrameBuilder(message.messageData);
                    int info_size = builder.popData(2);

                    for (int i = 0; i < info_size; i++) {
                        TestInfosStruct info = new TestInfosStruct();
                        info.index = builder.popData(1);
                        int str_len = builder.popData(2);
                        byte[] str_bytes = builder.popBytes(str_len);
                        if (str_bytes == null)
                            break;
                        info.name = new String(str_bytes);
                        info.type = builder.popData(1);
                        info.componentId=componentId;
                        infos.add(info);
                    }
                    mTestInfos = infos.toArray(new TestInfosStruct[infos.size()]);
                }
                return true;
            }
        };
        int result=pipe.synchronousPipe(request,callback);
        if(result==PipeResultDefine.SYNC_BUSY)
            return StateEventDefine.EVT_EXECUTE;    /*busy*/
        if(result==PipeResultDefine.SYNC_ERROR)
            return StateEventDefine.EVT_DISCONNECT;     /*disconnect*/
        return StateEventDefine.EVT_NONE;
    }

    public TestInfosStruct[] getTestInfos(){
        return mTestInfos;
    }

    private @StateEventDefine.StateEvent int getDynamicTestInfos(Pipe pipe){
        SparseArray<ComponentStruct>componentMap= Machine.getComponentList();
        int result=StateEventDefine.EVT_NONE;
        mDynamicTestInfos.clear();
        for(int i=0;i<componentMap.size();i++){
            result=getTestInfos(pipe,(short)componentMap.keyAt(i));
            if(result!=StateEventDefine.EVT_NONE)
                return result;
            TestInfosStruct[] infos=getTestInfos();
            for(TestInfosStruct info:infos){
                if(info.type==TestTypeDefine.DYNAMIC_TEST)
                    mDynamicTestInfos.add(info);
            }
        }
        return result;
    }

    public @StateEventDefine.StateEvent int dynamicComponentTest(Pipe pipe){
        int result=getDynamicTestInfos(pipe);
        if(result!=StateEventDefine.EVT_NONE){
            return result;
        }
        SystemClock.sleep(500);
        List<TestResultStruct> testResults=new ArrayList<>();
        mDynamicTestResults=null;
        for(TestInfosStruct info:mDynamicTestInfos){
            result=componentTest(pipe,info.componentId,(short)info.index);
            if(result!=StateEventDefine.EVT_NONE){
                return result;
            }
            TestResultStruct testResult=new TestResultStruct();
            testResult.name=info.name;
            testResult.result=getTestResult();
            testResults.add(testResult);
            SystemClock.sleep(100);
        }
        mDynamicTestResults=testResults.toArray(new TestResultStruct[testResults.size()]);
        return result;
    }

    public TestResultStruct[] getDynamicTestResult(){
        return mDynamicTestResults;
    }
}
